﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MC_AI_002
{
    /* Manages Track and TrackGroup Objects via a simple interface so that the 
     * programmer does not need all of the ins and outs of the stuff to 
     * get things to work properly. Does most of the hard labor autonomously
     */
    class TrackManager
    {
        /* Private variable declarations */
        private List<TrackGroup> TG_List;

        public TrackManager() 
        { 
            //Console.Write("TrackManager Initialized!\n");

            TG_List = new List<TrackGroup>();
            // add a TrackGroup to TG_List so that it does not have to be done later; good for initialization purposes
            TG_List.Add(new TrackGroup());

        } // for now will just do nothing but confirm initialization

        ~TrackManager() { 
            
            Console.WriteLine("TrackManager Destroyed!");
            foreach (TrackGroup TG in TG_List)
            {
                // iterate through and empty each list
                TG.clearList();
            }
            TG_List.RemoveRange(0,TG_List.Count());
            TG_List = null;
        
        } // does nothing but confirm object destruction

        private TrackGroup searchForContainingTrackGroup(int t_ID)
        {
            foreach (TrackGroup TG in TG_List){
                if (TG.getNumMembers() > 0){
                    if (TG.contains(t_ID)){
                        Console.WriteLine("\nTrack[{0}] found in TrackGroup[{1}]\n", t_ID, TG.getID());
                        return TG;
                    }
                }
            }
            // if a valid TG is not found, return null
            return null;
        }
        private void printSubTracks(List<Track>[] subTracks)
        {
            for (int i = 0; i < subTracks.Length; i++)
            {
                Console.WriteLine("SubTrack[{0}] has {1} members", i, subTracks[i].Count());
                // print the members of subTracks[i]
                foreach (Track t_subTrack in subTracks[i])
                {
                    if (t_subTrack != null)
                    {
                        Console.WriteLine("\tTrack[{0}]", t_subTrack.getID());
                    }
                }
            }
        }

        // retroactively removes track[t_ID] when it clears out the data of t_TG and then doesn't add it back into other groups
        public bool removeTrack(int t_ID) // find and remove track by track ID
        {
            // first get the trackgroup that t_ID is a part of
            TrackGroup t_TG = searchForContainingTrackGroup(t_ID); // a valid TrackGroup if found, null if not found
            
            // check to see if t_TG is valid
            if (t_TG != null)
            {
                // now get the Track and its neighbors
                Track t_Track = t_TG.getTrackByID(t_ID);
                Track[] t_Neighbors = new Track[t_Track.getNumNeighbors()];
                int[] t_TrackNeighborIDs = t_Track.getNeighbors();
                    // populate t_Neighbors
                for (int i = 0; i < t_TrackNeighborIDs.Length; i++)
                {
                    t_Neighbors[i] = t_TG.getTrackByID(t_TrackNeighborIDs[i]);
                }

                    // remove references between t_Track and t_Neighbors
                foreach (Track T in t_Neighbors) // not removing neighbors properly
                {
                    T.removeNeighbor(t_Track.getID());
                    t_Track.removeNeighbor(T.getID());
                    //T.removeNeighbor(T.getID());
                }
                
                    // get a list of subtracks
                List<Track>[] subTracks = TrackIterator.findBrokenPaths(t_TG, t_Neighbors);

                // if subTracks for some reason == null then end this method
                if (subTracks == null)
                {
                    return false;
                }// otherwise just continue as normal

                // print subTracks list
                printSubTracks(subTracks);

                // since we have our list of subtracks, check if the number of subtracks is > 1; if so then empty t_TG and discard it
                // so we can populate it anew as well as any other empty TrackGroups first
                if (subTracks.Length > 1)
                {
                    t_TG.clearList();

                    // now that t_TG is clear we can start on populating everything
                    for (int i = 0; i < subTracks.Length; i++)
                    {// iterate through each subTracks list; try to find an empty list to populate or, failing that, create a new one to populate
                        bool foundEmpty = false;
                        // first, look through TG_List to find an empty list
                        foreach (TrackGroup t__TG in TG_List)
                        {
                            if (t__TG.getNumMembers() == 0) // empty list found!
                            {
                                // now iterate through subTracks[i] Track members and push them into t__TG
                                foreach (Track t__Track in subTracks[i])
                                {
                                    // make sure that they are not adding in Track[t_ID] while going through this
                                    if (t__Track.getID() != t_ID)
                                    {
                                        t__TG.addTrack(t__Track);
                                    }
                                }
                                // we have found an empty one so no need to keep searching
                                foundEmpty = true;
                                break;
                            }
                        }
                        // if we are unable to find an empty TrackGroup we need to make one
                        if (!foundEmpty)
                        {
                            TG_List.Add(new TrackGroup(subTracks[i])); // automatically populates TrackGroup
                            // need to remove Track[t_ID] from this new TrackGroup
                            //Track vt_Track = TG_List.Last().getTrackByID(t_ID);
                            //TG_List.Last().removeTrack(vt_Track);
                        }
                    }
                }// otherwise do nothing else and no tracks have to be moved around so go ahead and exit by returning True

                return true;
            }
            else
            {
                Console.WriteLine("Track[{0}] Not found!", t_ID);
                return false; // in this case the t_ID is not found
            }
        }
        public bool addTrack(float[] xyz, float[] abc)
        {
            // Create a dummy Track object to place into a TrackGroup
            Track dummy = new Track(xyz, abc), cullTrack;

            // Iterator that is incremented whenever the dummy track fulfulls the requirements to be added to a TrackGroup
            int numValidConnections = 0;
            float maxDistance = 1.2f; // used as first cull value

            List<Track> TG_F_CullIDs = new List<Track>();
            

            // See if this dummy track can connect to any locations within any TrackGroups in TG_List
            foreach (TrackGroup TG in TG_List){
                // disregard any TG that has zero members
                //Console.WriteLine("TG.getNumMembers = {0}", TG.getNumMembers());
                if (TG.getNumMembers() > 0) // only do something with TG if it has at least 1 member
                {
                    // some crap to cull out any TGs that are far away ( distance > maxDistance from any/all members)
                    
                    cullTrack = (TG.getClosestMember(dummy, maxDistance)); // just add the IDs to the list to make things easier
                    
                    if (cullTrack != null)
                    {
                        //Console.WriteLine("cullTrack = {0}", cullTrack.getID());
                        TG_F_CullIDs.Add(cullTrack);
                    }
                }
            }
            // now that TG_F_CullIDs has been populated, if it has been populated that is, now check to see if dummy track can connect to any
            // of the Tracks in TG_F_CullIDs
            foreach (Track i in TG_F_CullIDs)
            {
                // for now let's just say that if the item is in TG_F_CullIDs then it is a valid connection
                numValidConnections++;
            }
            //Console.WriteLine("numValidConnections = {0}", numValidConnections);
            //Console.WriteLine("TG_F_CullIDs.Count = {0}", TG_F_CullIDs.Count());
            if (numValidConnections == 0)
            {
                // try to find a TrackGroup with 0 members, if none are found then create new
                foreach (TrackGroup TG in TG_List)
                {
                    if (TG.getNumMembers() == 0)
                    {
                        TG.addTrack(dummy);
                        return true; // easy way to scoot out of this section quickly
                    }
                }
                // if no empty track group is found, create a new one!
                TG_List.Add(new TrackGroup(new Track(dummy)));
                
                return true;
            }
            else // there are valid connections, now just need to figure out how to work them in together
            {
                // print out all connecting IDs
                /*
                foreach (Track T in TG_F_CullIDs)
                {
                    Console.WriteLine("\tConnection: Track[{0}]", T.getID());
                }
                Console.WriteLine();
                */
                if (numValidConnections == 1)
                {
                    foreach (TrackGroup TG in TG_List)
                    {
                        // for each TrackGroup in the TrackGroup list search for the first valid connection in TG_F_CullIDs
                        if (TG.contains(TG_F_CullIDs.ElementAt(0)))
                        {
                            dummy.addNeighbor(TG_F_CullIDs.ElementAt(0).getID());
                            TG_F_CullIDs.ElementAt(0).addNeighbor(dummy.getID());

                            TG.addTrack(dummy);
                        }
                    }
                }
                else
                {
                    // now to figure out which TrackGroups are being connected
                    List<TrackGroup> TrackGroupConnectionSet = new List<TrackGroup>();

                    foreach (TrackGroup TG in TG_List)
                    {
                        foreach (Track T in TG_F_CullIDs)
                        {
                            if (TG.contains(T))
                            {
                                TrackGroupConnectionSet.Add(TG);
                                break; // makes sure that a TrackGroup is only added to the ConnectionSet once
                            }
                        }
                    }

                    foreach (Track T in TG_F_CullIDs)
                    {
                        dummy.addNeighbor(T.getID());
                        T.addNeighbor(dummy.getID());
                    }

                    foreach (TrackGroup TG in TrackGroupConnectionSet)
                    {
                        if (TG == TrackGroupConnectionSet.ElementAt(0))
                        {
                            continue; // skip the first group because it will always be the lowest index TrackGroup
                        }
                        else
                        {
                            TrackGroupConnectionSet.ElementAt(0).merge(TG.getAllTracks());
                            TG.clearList();
                        }
                    }
                    TrackGroupConnectionSet.ElementAt(0).addTrack(dummy);
                    
                }
                return false;
            }
        }

        public void printItems(bool groups, bool tracks)
        {
            Console.WriteLine("TRACKMANAGER (TMAN) Member List");
            Console.WriteLine("\tShow Groups: " + groups);
            Console.WriteLine("\tShow Tracks: " + tracks);

            foreach (TrackGroup TG in TG_List)
            {
                if (groups)
                {
                    Console.Write("TrackGroup[{0}] has {1} Tracks\n", TG.getID(), TG.getNumMembers());
                }
                if (tracks){
                    TG.printMembers();
                }
            }
        }

    }
}
